import os
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),'modules'))
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
import ganzhi
import week_yun
import ktt_order_export
import ktt_core
import ktt_core_new
import pandas as pd
import read_config
from flask import Flask, request,jsonify, Response,render_template,send_file,make_response
import zipfile
import io
from datetime import datetime,timedelta
import json
import xlwings as xw
import openpyxl
from openpyxl.utils.dataframe import dataframe_to_rows
import pymysql
import platform
if platform.system().lower()=='windows':
    import pythoncom
    import win32com.client
elif platform.system().lower()=='darwin':
    pass

import dd_ppt
import base64
import subprocess
import random

from PIL import Image
import re

class EjService(Flask):

    def __init__(self,*args,**kwargs):
        super(EjService, self).__init__(*args, **kwargs)
        config_fn=os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),'configs','ej_service.config')
        self.config_ej=read_config.read_json(config_fn)

        #读取快团团有关配置文件存放路径
        ktt_config=os.path.join(os.path.dirname(__file__),'config','ktt.config')
        with open(ktt_config,'r',encoding='utf-8') as f:
            self.ktt_config=json.loads(f.read())

        # print(self.ktt_config)
        #读取不同发货商导表时与快团团对应的规格名称
        with open(self.ktt_config['ktt_col_map_config'],'r',encoding='utf-8') as f:
            self.col_map_config=json.loads(f.read())

        # 读取不同发货商发货表的列名
        with open(self.ktt_config['col_config_fn'],'r',encoding='utf-8') as f:
            self.config_ktt_order=json.loads(f.read())

        # 读取规格的默认名称返回前端
        with open(self.ktt_config['page_config_fn'],'r',encoding='utf-8') as f:
            config_page_default=json.loads(f.read())
        self.spec0=config_page_default['spec0']
        self.spec1=config_page_default['spec1']
        self.fn_info=config_page_default['fn_info']
        self.sender_name=config_page_default['sender_name']
        self.sender_tel=config_page_default['sender_tel']

        
        #路由
       
        #Markdown图片转视频
        self.add_url_rule('/md_pic_to_video',view_func=self.md_pic_to_video,methods=['POST'])     
        #处理学习星球markdown转图片
        self.add_url_rule('/md_to_pic', view_func=self.md_to_pic,methods=['GET','POST'])  

    
    def md_to_pic(self):
        return render_template('/markdown_to_pic.html')

    def md_pic_to_video(self):
        print('generate video...')
        temp_dir = None
        img_paths = []
        output_path = None
        try:
            data = request.json
            images = data.get('images', [])
            # print('images:',images)
            resolution = data.get('resolution', '960x1280')
            
            if not images:
                return jsonify({'res': 'failed', 'msg': '没有接收到图片数据'})
            
            # 从配置文件读取临时目录
            cfg = os.path.join(os.path.dirname(__file__), 'config', 'dogduck.config')
            print(f"配置文件路径: {cfg}")
            print(f"文件是否存在: {os.path.exists(cfg)}")
            
            try:
                with open(cfg, 'r',encoding='utf-8') as f:
                    print("成功打开配置文件")
                    config = json.load(f)
                    print('config内容:', config)
                    temp_dir = config.get('temp_video_dir', 'temp_video')
                    print('使用的临时目录:', temp_dir)
                    os.makedirs(temp_dir, exist_ok=True)
            except json.JSONDecodeError as e:
                print(f"JSON解析错误: {str(e)}")
                raise
            except Exception as e:
                print(f"配置文件读取错误: {str(e)}")
                raise
            
            # 下载并验证图片到临时目录
            for i, img_data in enumerate(images):
                try:
                    # 检查数据是否包含base64前缀
                    if not img_data.startswith('data:image/'):
                        raise ValueError('无效的图片格式')
                        
                    # 分离base64数据部分
                    header, encoded = img_data.split(',', 1)
                    img_format = header.split('/')[1].split(';')[0]
                    
                    # 解码并验证图片
                    decoded = base64.b64decode(encoded)
                    img = Image.open(io.BytesIO(decoded))
                    img.verify()  # 验证图片完整性
                    
                    # 保存图片
                    img_path = os.path.join(temp_dir, f'img_{i}.{img_format}')
                    with open(img_path, 'wb') as f:
                        f.write(decoded)
                    img_paths.append(img_path)
                    
                except Exception as e:
                    print(f"图片{i}无效: {str(e)}")
                    continue
            
            # 使用FFmpeg创建视频
            output_path = os.path.join(temp_dir, 'output.mp4')
            ffmpeg=config.get('ffmpeg','ffmpeg')
            ffprobe=config.get('ffprobe','ffprobe')

            # 获取每张图片显示时间
            time_per_pic=str(data.get('time_per_pic',4))

            print(ffmpeg,ffprobe)
            
            # 处理音乐文件
            bg_audio = None
            duration = None
            
            # 1. 优先处理上传音乐
            if data.get('music_file_data'):
                try:
                    bg_audio = self._process_uploaded_music(data, temp_dir, ffmpeg)
                except Exception as e:
                    print(f"处理上传音乐失败: {str(e)}")
                    raise
            
            # 2. 如果没有上传音乐，处理预设音乐
            if bg_audio is None and data.get('music_style'):
                try:
                    bg_audio = self._process_preset_music(data, config, temp_dir)
                    if bg_audio is None:
                        print("警告: 预设音乐目录中没有可用的MP3文件")
                except Exception as e:
                    print(f"处理预设音乐失败: {str(e)}")
                    raise
            
            # 生成无音频视频(无论是否有音乐都先生成无音频版本)
            video_cmd = [
                ffmpeg,
                '-framerate', f'1/{time_per_pic}',
                '-i', f'{temp_dir}/img_%d.png',
                '-c:v', 'libx264',
                '-r', '30',
                '-pix_fmt', 'yuv420p',
                '-movflags', '+faststart',
                '-profile:v', 'main',
                '-preset', 'fast',
                '-crf', '23',
                '-an',  # 无音频
                '-y',  # 覆盖输出文件
                output_path
            ]
            subprocess.run(video_cmd, check=True)
            
            # 获取视频时长
            probe_cmd = [ffprobe, '-v', 'error', '-show_entries', 'format=duration', 
                       '-of', 'default=noprint_wrappers=1:nokey=1', output_path]
            duration = float(subprocess.check_output(probe_cmd).decode().strip())
            
            # 如果有音乐，合并音频
            if bg_audio:
                try:
                    bg_audio, fade_params = self._process_music(bg_audio, duration, temp_dir, ffmpeg, ffprobe)
                    
                    # 合并音频(带淡出效果)
                    final_output = os.path.join(temp_dir, 'final_output.mp4')
                    audio_cmd = [
                        ffmpeg,
                        '-i', output_path,
                        '-i', bg_audio,
                        '-filter_complex', 
                        f'[1:a]afade={fade_params}[a1];[a1]volume=0.5[a2]',
                        '-map', '0:v',
                        '-map', '[a2]',
                        '-c:v', 'copy',
                        '-c:a', 'aac',  # 明确指定音频编码
                        '-b:a', '192k',  # 设置音频比特率
                        '-shortest',
                        '-y',  # 覆盖输出文件
                        final_output
                    ]
                    print("执行音频合并命令:", " ".join(audio_cmd))
                    result = subprocess.run(audio_cmd, check=True, capture_output=True, text=True, encoding='utf-8', errors='ignore')
                    print("命令输出:", result.stdout)
                    print("命令错误:", result.stderr)
                    os.replace(final_output, output_path)
                    print("视频文件替换完成")
                    
                    # 如果是上传音乐，需要额外清理临时文件
                    if 'uploaded_music' in bg_audio:
                        os.remove(bg_audio)
                        print(f"已删除上传的音乐文件: {bg_audio}")
                except Exception as e:
                    print(f"合并音频失败: {str(e)}")
                    raise
            
            # 返回视频文件
            if not os.path.exists(output_path):
                raise FileNotFoundError('视频文件生成失败')
            
            # 验证视频文件
            print('验证视频文件')
            try:
                subprocess.run([ffprobe, output_path], check=True, capture_output=True)
            except subprocess.CalledProcessError:
                raise ValueError('生成的视频文件格式无效')
            
            # 确保文件可读并验证大小
            file_size = os.path.getsize(output_path)
            print(f"原始文件大小: {file_size} 字节")
            
            with open(output_path, 'rb') as f:
                video_data = f.read()
                print(f"实际读取大小: {len(video_data)} 字节")
                if len(video_data) != file_size:
                    raise ValueError(f"文件读取不完整: {len(video_data)}/{file_size} 字节")
            
            # 验证文件可读并验证大小
            file_size = os.path.getsize(output_path)
            print(f"原始文件大小: {file_size} 字节")
            
            # 使用chunked传输确保大文件传输
            def generate():
                with open(output_path, 'rb') as f:
                    while True:
                        chunk = f.read(8192)
                        if not chunk:
                            break
                        yield chunk
            
            # 创建响应对象
            response = Response(generate(), mimetype='video/mp4')
            response.headers['Content-Disposition'] = 'attachment; filename=markdown_video.mp4'
            response.headers['Content-Length'] = str(file_size)
            print(f"响应头设置完成，Content-Length: {file_size}")
            
            # 记录临时文件路径用于调试
            print(f"视频文件路径: {output_path}")
            
            # # 异步清理临时文件
            # import threading
            # t = threading.Timer(60.0, self._cleanup_temp_files, [temp_dir, img_paths, output_path])
            # t.start()
            
            return response
            
        except Exception as e:
            return jsonify({'res': 'failed', 'msg': f'视频生成失败: {str(e)}'})
        finally:
            # 确保无论如何都尝试清理
            self._cleanup_temp_files(temp_dir, img_paths, output_path)

    def _process_uploaded_music(self, data, temp_dir, ffmpeg):
        """处理上传的音乐文件"""
        try:
            music_data = data['music_file_data']
            file_ext = music_data['name'].split('.')[-1].lower()
            temp_path = os.path.join(temp_dir, f'uploaded_music.{file_ext}')
            
            # 保存并处理上传音乐
            with open(temp_path, 'wb') as f:
                f.write(base64.b64decode(music_data['content']))
            
            # 如果是MP4则提取音频
            if file_ext == 'mp4':
                audio_path = os.path.join(temp_dir, 'uploaded_music.mp3')
                cmd = [ffmpeg, '-i', temp_path, '-vn', '-acodec', 'libmp3lame', '-y', audio_path]
                subprocess.run(cmd, check=True)
                os.remove(temp_path)
                return audio_path
            return temp_path
        except Exception as e:
            print(f"处理上传音乐失败: {str(e)}")
            if 'temp_path' in locals() and os.path.exists(temp_path):
                os.remove(temp_path)
            if 'audio_path' in locals() and os.path.exists(audio_path):
                os.remove(audio_path)
            raise

    def _process_preset_music(self, data, config, temp_dir):
        """处理预设音乐"""
        try:
            music_style = data.get('music_style','modern')
            bg_audio_dir = config.get('bg_audio_chinese_dir' if music_style == 'traditional' else 'bg_audio_dir')
            
            if bg_audio_dir and os.path.exists(bg_audio_dir):
                mp3_files = [f for f in os.listdir(bg_audio_dir) if f.endswith('.mp3')]
                if mp3_files:
                    return os.path.join(bg_audio_dir, random.choice(mp3_files))
            return None
        except Exception as e:
            print(f"处理预设音乐失败: {str(e)}")
            raise

    def _process_music(self, bg_audio, video_duration, temp_dir, ffmpeg, ffprobe):
        """处理背景音乐(上传或预设)"""
        try:
            # 获取音频时长
            audio_duration_cmd = [ffprobe, '-v', 'error', '-show_entries', 
                                'format=duration', '-of', 
                                'default=noprint_wrappers=1:nokey=1', bg_audio]
            audio_duration = float(subprocess.check_output(audio_duration_cmd).decode().strip())
            
            # 如果音频长度不足，循环拼接
            if audio_duration < video_duration:
                loop_times = int(video_duration / audio_duration) + 1
                looped_audio = os.path.join(temp_dir, 'looped_audio.mp3')
                loop_cmd = [
                    ffmpeg,
                    '-stream_loop', str(loop_times),
                    '-i', bg_audio,
                    '-c', 'copy',
                    '-y', looped_audio
                ]
                subprocess.run(loop_cmd, check=True)
                bg_audio = looped_audio
            
            # 返回处理后的音频路径和淡出参数
            return bg_audio, f't=out:st={video_duration-1.8}:d=1.8'
        except Exception as e:
            print(f"处理音乐文件时出错: {str(e)}")
            raise

    def _cleanup_temp_files(self, temp_dir, img_paths, output_path):
        """统一的临时文件清理方法"""
        try:
            print(f"开始清理临时目录: {temp_dir}")
            # 删除图片文件
            for img_path in img_paths:
                if img_path and os.path.exists(img_path):
                    os.remove(img_path)
                    print(f"已删除图片: {img_path}")
            # 删除上传的音乐文件和循环音频
            music_path = os.path.join(temp_dir, 'uploaded_music.mp3')
            looped_audio = os.path.join(temp_dir, 'looped_audio.mp3')
            if os.path.exists(music_path):
                os.remove(music_path)
                print(f"已删除音乐文件: {music_path}")
            if os.path.exists(looped_audio):
                os.remove(looped_audio)
                print(f"已删除循环音频: {looped_audio}")
            # 删除视频文件
            # if output_path and os.path.exists(output_path):
            #     os.remove(output_path)
            #     print(f"已删除视频: {output_path}")
            print("临时文件清理完成")
        except Exception as e:
            print(f"清理临时文件时出错: {str(e)}")
            # 记录更详细的错误信息
            import traceback
            traceback.print_exc()

if __name__ == '__main__':
    app = EjService(__name__)
    if len(sys.argv)>1:
        # print(f'服务器为：{sys.argv[1]}:5000')
        app.run(debug=True,host=sys.argv[1],port=5023)
    else:
        app.run(debug=True)

    # app.run(debug=True,host='127.0.0.1',port=5023)
    # app.run(debug=True,host='192.168.10.2',port=5000)
    # app.run(debug=True,host='192.168.1.41',port=5001)
    # app.run(debug=True,host='192.168.1.149',port=5000)
    # res=wecom_dir()
    # print(res)
